﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;

namespace KaoQin.RFIDLIB
{
    public class CardFun : ICardFun
    {
        #region 变量
        public const byte BLOCK0_EN = 0x01;//操作第0块
        public const byte BLOCK1_EN = 0x02;//操作第1块
        public const byte BLOCK2_EN = 0x04;//操作第2块
        public const byte NEEDSERIAL = 0x08;//仅对指定序列号的卡操作
        public const byte EXTERNKEY = 0x10;
        public const byte NEEDHALT  = 0x20;
        #endregion

        #region 私有方法

        /// <summary>
        /// 让设备发出声响
        /// </summary>
        /// <param name="xms">响声的时间长度，单位为毫秒</param>
        /// <returns></returns>
        [DllImport("OUR_MIFARE.dll", EntryPoint = "pcdbeep")]
        static extern byte pcdbeep(int xms);

        /// <summary>
        /// 读取设备编号，可做为软件加密狗用,也可以根据此编号在公司网站上查询保修期限
        /// </summary>
        /// <param name="devicenumber">r用于返回编号</param>
        /// <returns></returns>
        [DllImport("OUR_MIFARE.dll", EntryPoint = "pcdgetdevicenumber")]
        static extern byte pcdgetdevicenumber(byte[] devicenumber);//devicenumber用于返回编号 

        /// <summary>
        /// 轻松读卡
        /// </summary>
        /// <param name="ctrlword">控制字</param>
        /// <param name="serial">卡序列号数组，用于指定或返回卡序列号</param>
        /// <param name="area">指定读卡区号</param>
        /// <param name="keyA1B0">指定用A或B密码认证,一般是用A密码，只有特殊用途下才用B密码，在这不做详细解释</param>
        /// <param name="picckey">指定卡密码，6个字节，卡出厂时的初始密码为6个0xff</param>
        /// <param name="piccdata0_2">用于返回卡该区第0块到第2块的数据，共48个字节</param>
        /// <returns></returns>
        [DllImport("OUR_MIFARE.dll", EntryPoint = "piccreadex")]
        static extern byte piccreadex(byte ctrlword, byte[] serial, byte area, byte keyA1B0, byte[] picckey, byte[] piccdata0_2);

        /// <summary>
        /// 轻松写卡
        /// </summary>
        /// <param name="ctrlword">控制字</param>
        /// <param name="serial">卡序列号数组，用于指定或返回卡序列号</param>
        /// <param name="area">指定读卡区号</param>
        /// <param name="keyA1B0">指定用A或B密码认证,一般是用A密码，只有特殊用途下才用B密码，在这不做详细解释</param>
        /// <param name="picckey">指定卡密码，6个字节，卡出厂时的初始密码为6个0xff</param>
        /// <param name="piccdata0_2">用于写入卡该区第0块到第2块的数据，共48个字节</param>
        /// <returns></returns>
        [DllImport("OUR_MIFARE.dll", EntryPoint = "piccwriteex")]
        static extern byte piccwriteex(byte ctrlword, byte[] serial, byte area, byte keyA1B0, byte[] picckey, byte[] piccdata0_2);

        /// <summary>
        /// 修改卡单区的密码
        /// </summary>
        /// <param name="ctrlword">控制字</param>
        /// <param name="serial">卡序列号数组，用于指定或返回卡序列号</param>
        /// <param name="area">指定读卡区号</param>
        /// <param name="keyA1B0">指定用A或B密码认证,一般是用A密码，只有特殊用途下才用B密码，在这不做详细解释</param>
        /// <param name="piccoldkey">旧密码</param>
        /// <param name="piccnewkey">新密码</param>
        /// <returns></returns>
        [DllImport("OUR_MIFARE.dll", EntryPoint = "piccchangesinglekey")]
        static extern byte piccchangesinglekey(byte ctrlword, byte[] serial, byte area, byte keyA1B0, byte[] piccoldkey, byte[] piccnewkey);

        /// <summary>
        /// 取得卡序列号
        /// </summary>
        /// <param name="serial">返回的卡序列号数组</param>
        /// <returns></returns>
        [DllImport("OUR_MIFARE.dll", EntryPoint = "piccrequest")]
        static extern byte piccrequest(byte[] serial);

        private bool splitAreaData(byte[] input, out List<byte[]> newData)
        {
            try
            {
                int AreaCount = (int)Math.Ceiling(input.Length / 48.0m);
                List<byte[]> result = new List<byte[]>();
                for (int i = 0; i < AreaCount; i++)
                {
                    int startPoint = i * 48;
                    int endPoint = startPoint + 48 - 1;

                    if ((input.Length - 1) < endPoint)
                    {
                        endPoint = input.Length - 1;
                    }

                    int length = endPoint - startPoint + 1;

                    byte[] temp = new byte[48];

                    Buffer.BlockCopy(input, startPoint, temp, 0, length);

                    result.Add(temp);

                }

                newData = result;

                return true;

            }
            catch (Exception ex)
            {
                newData = null;
                return false;
            }

        }
        #endregion

        #region 公有方法

        /// <summary>
        /// 让设备发出声响
        /// </summary>
        /// <param name="xms"></param>
        /// <returns></returns>
        public int DoBeed(int xms)
        {
            return pcdbeep(xms);
        }


        /// <summary>
        /// 获取设备编号
        /// </summary>
        /// <param name="deviceNumber"></param>
        /// <returns></returns>
        public int GetDeviceNumber(out string deviceNumber)
        {
            byte[] deviceNum = new byte[4];
            byte state = pcdgetdevicenumber(deviceNum);
            deviceNumber = string.Empty;

            if (state == 0)
            {
                deviceNumber = Convert.ToString(deviceNum[0]) + "-" + Convert.ToString(deviceNum[1]) + "-" + Convert.ToString(deviceNum[2]) + "-" + Convert.ToString(deviceNum[3]);
            }

            return state;
        }

        /// <summary>
        /// 读取用户存储区数据
        /// </summary>
        /// <param name="key">密码，为空则默认使用原始密码</param>
        /// <param name="userData"></param>
        /// <returns></returns>
        public int GetUserDatas(string key, out string userData)
        {
            userData = string.Empty;

            byte status = 0;//存放返回值
            byte myareano;//区号
            byte authmode;//密码类型，用A密码或B密码
            byte myctrlword;//控制字
            byte[] mypicckey = new byte[6] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };//密码
            byte[] mypiccserial = new byte[4];//卡序列号

            //控制字指定,控制字的含义请查看本公司网站提供的动态库说明
            myctrlword = BLOCK0_EN + BLOCK1_EN + BLOCK2_EN + EXTERNKEY;
            //批定密码模式
            authmode = 1;//大于0表示用A密码认证，推荐用A密码认证
            List<byte> udatas = new List<byte>();

            if (!string.IsNullOrEmpty(key) && key.Length != 6)
            {
                return -1;
            }
            if (!string.IsNullOrEmpty(key))
            {
                mypicckey = Encoding.ASCII.GetBytes(key);
            }

            for (int i = 5; i <= 15; i++)
            {
                byte[] mypiccdata = new byte[48]; //卡数据缓冲
                myareano = (byte)i;
                status = piccreadex(myctrlword, mypiccserial, myareano, authmode, mypicckey, mypiccdata);
                if (status != 0)
                {
                    return status;
                }
                else
                {
                    udatas.AddRange(mypiccdata);
                }
            }

            userData = Regex.Replace(Encoding.Default.GetString(udatas.ToArray()), "[\0]{1,}$", "");

            return status;

        }

        /// <summary>
        /// 写入用户存储区数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="userData"></param>
        /// <returns></returns>
        public int WriteUserDatas(string key, string userData)
        {


            if (string.IsNullOrEmpty(userData))
            {
                return 0;
            }

            byte status;//存放返回值
            byte myareano;//区号
            byte authmode;//密码类型，用A密码或B密码
            byte myctrlword;//控制字
            byte[] mypicckey = new byte[6] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };//密码
            byte[] mypiccserial = new byte[4];//卡序列号

            //控制字指定,控制字的含义请查看本公司网站提供的动态库说明

            myctrlword = BLOCK0_EN + BLOCK1_EN + BLOCK2_EN + EXTERNKEY + NEEDHALT;

            //批定密码模式
            authmode = 1;//大于0表示用A密码认证，推荐用A密码

            if (!string.IsNullOrEmpty(key) && key.Length != 6)
            {
                return -1;
            }
            if (!string.IsNullOrEmpty(key))
            {
                mypicckey = Encoding.ASCII.GetBytes(key);
            }

            byte[] inputData = Encoding.Default.GetBytes(userData);

            if (inputData.Length > 528)
            {
                return -2;
            }
            List<byte[]> sdata = new List<byte[]>();

            if (splitAreaData(inputData, out sdata))
            {
                for (int i = 0; i < sdata.Count; i++)
                {
                    myareano = (byte)(i + 5);
                    status = piccwriteex(myctrlword, mypiccserial, myareano, authmode, mypicckey, sdata[i]);

                    if (status != 0)
                    {
                        return status;
                    }
                }
            }

            return 0;

        }

        /// <summary>
        /// 读取系统存储区数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="sysData"></param>
        /// <returns></returns>
        public int GetSysDatas(string key, out string sysData)
        {
            sysData = string.Empty;

            byte status = 0;//存放返回值
            byte myareano;//区号
            byte authmode;//密码类型，用A密码或B密码
            byte myctrlword;//控制字
            byte[] mypicckey = new byte[6] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };//密码
            byte[] mypiccserial = new byte[4];//卡序列号

            //控制字指定,控制字的含义请查看本公司网站提供的动态库说明
            myctrlword = BLOCK0_EN + BLOCK1_EN + BLOCK2_EN + EXTERNKEY;
            //批定密码模式
            authmode = 1;//大于0表示用A密码认证，推荐用A密码认证
            List<byte> udatas = new List<byte>();

            if (!string.IsNullOrEmpty(key) && key.Length != 6)
            {
                return -1;
            }
            if (!string.IsNullOrEmpty(key))
            {
                mypicckey = Encoding.ASCII.GetBytes(key);
            }

            for (int i = 2; i <= 4; i++)
            {
                byte[] mypiccdata = new byte[48]; //卡数据缓冲
                myareano = (byte)i;
                status = piccreadex(myctrlword, mypiccserial, myareano, authmode, mypicckey, mypiccdata);
                if (status != 0)
                {
                    return status;
                }
                else
                {
                    udatas.AddRange(mypiccdata);
                }
            }

            sysData = Regex.Replace(Encoding.Default.GetString(udatas.ToArray()), "[\0]{1,}$", "");

            return status;
        }

        /// <summary>
        /// 写入系统存储区数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="sysData"></param>
        /// <returns></returns>
        public int WriteSysDatas(string key, string sysData)
        {
            if (string.IsNullOrEmpty(sysData))
            {
                return 0;
            }

            byte status;//存放返回值
            byte myareano;//区号
            byte authmode;//密码类型，用A密码或B密码
            byte myctrlword;//控制字
            byte[] mypicckey = new byte[6] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };//密码
            byte[] mypiccserial = new byte[4];//卡序列号
            //控制字指定,控制字的含义请查看本公司网站提供的动态库说明

            myctrlword = BLOCK0_EN + BLOCK1_EN + BLOCK2_EN + EXTERNKEY + NEEDHALT;

            //批定密码模式
            authmode = 1;//大于0表示用A密码认证，推荐用A密码

            if (!string.IsNullOrEmpty(key) && key.Length != 6)
            {
                return -1;
            }
            if (!string.IsNullOrEmpty(key))
            {
                mypicckey = Encoding.ASCII.GetBytes(key);
            }

            byte[] inputData = Encoding.Default.GetBytes(sysData);

            if (inputData.Length > 144)
            {
                return -3;
            }
            List<byte[]> sdata = new List<byte[]>();

            if (splitAreaData(inputData, out sdata))
            {
                for (int i = 0; i < sdata.Count; i++)
                {
                    myareano = (byte)(i + 2);
                    status = piccwriteex(myctrlword, mypiccserial, myareano, authmode, mypicckey, sdata[i]);

                    if (status != 0)
                    {
                        return status;
                    }
                }
            }

            return 0;
        }

        /// <summary>
        /// 修改IC卡密码
        /// </summary>
        /// <param name="oldKey"></param>
        /// <param name="newKey"></param>
        /// <returns></returns>
        public int ChangePass(string oldKey, string newKey)
        {
            byte status = 0;//存放返回值
            byte myareano;//区号
            byte authmode;//密码类型，用A密码或B密码
            byte myctrlword;//控制字
            byte[] piccoldkey = new byte[6] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, };//旧密码
            byte[] mypiccserial = new byte[4];//卡序列号
            byte[] piccnewkey = new byte[6] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; //新密码.

            myctrlword = 0;
            //批定密码模式
            authmode = 1;//大于0表示用A密码认证，推荐用A密码认证

            if (!string.IsNullOrEmpty(oldKey) && oldKey.Length != 6)
            {
                return -1;
            }

            if (!string.IsNullOrEmpty(newKey) && newKey.Length != 6)
            {
                return -1;
            }

            if (!string.IsNullOrEmpty(oldKey))
            {
                piccoldkey = Encoding.ASCII.GetBytes(oldKey);
            }

            if (!string.IsNullOrEmpty(newKey))
            {
                piccnewkey = Encoding.ASCII.GetBytes(newKey);
            }

            for (int i = 0; i <= 15; i++)
            {
                myareano = (byte)i;
                status = piccchangesinglekey(myctrlword, mypiccserial, myareano, authmode, piccoldkey, piccnewkey);

                if (status != 0)
                {
                    return status;
                }
            }

            return status;

        }

        /// <summary>
        /// 读取卡序列号
        /// </summary>
        /// <param name="serial"></param>
        /// <returns></returns>
        public int GetCardSerial(out string serial)
        {
            byte[] b = new byte[4];
            byte status = 0; ;

            status = piccrequest(b);
            serial = string.Empty;
            if (status == 0)
            {
                serial = Convert.ToString(b[0]) + "-" + Convert.ToString(b[1]) + "-" + Convert.ToString(b[2]) + "-" + Convert.ToString(b[3]);
            }

            return status;

        }

        /// <summary>
        /// 写入卡唯一ID
        /// </summary>
        /// <param name="key"></param>
        /// <param name="uniqueID"></param>
        /// <returns></returns>
        public int WriteCardUniqueID(string key, Guid uniqueID)
        {

            byte status;//存放返回值
            byte myareano = 1;//区号
            byte authmode;//密码类型，用A密码或B密码
            byte myctrlword;//控制字
            byte[] mypicckey = new byte[6] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };//密码
            byte[] mypiccserial = new byte[4];//卡序列号
            //控制字指定,控制字的含义请查看本公司网站提供的动态库说明

            myctrlword = BLOCK0_EN + BLOCK1_EN + BLOCK2_EN + EXTERNKEY + NEEDHALT;

            //批定密码模式
            authmode = 1;//大于0表示用A密码认证，推荐用A密码

            if (!string.IsNullOrEmpty(key) && key.Length != 6)
            {
                return -1;
            }
            if (!string.IsNullOrEmpty(key))
            {
                mypicckey = Encoding.ASCII.GetBytes(key);
            }

            byte[] inputData = uniqueID.ToByteArray();
            byte[] sdata = new byte[48];

            for (int i = 0; i < inputData.Length; i++)
            {
                sdata[i] = inputData[i];
            }

            status = piccwriteex(myctrlword, mypiccserial, myareano, authmode, mypicckey, sdata);

            return status;
        }

        /// <summary>
        /// 读取卡唯一ID
        /// </summary>
        /// <param name="key"></param>
        /// <param name="uniqueID"></param>
        /// <returns></returns>
        public int GetCardUniqueID(string key, out Guid uniqueID)
        {
            uniqueID = Guid.Empty;

            byte status = 0;//存放返回值
            byte myareano = 1;//区号
            byte authmode;//密码类型，用A密码或B密码
            byte myctrlword;//控制字
            byte[] mypicckey = new byte[6] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };//密码
            byte[] mypiccserial = new byte[4];//卡序列号

            //控制字指定,控制字的含义请查看本公司网站提供的动态库说明
            myctrlword = BLOCK0_EN+ BLOCK1_EN + BLOCK2_EN + EXTERNKEY;
            //批定密码模式
            authmode = 1;//大于0表示用A密码认证，推荐用A密码认证
            

            if (!string.IsNullOrEmpty(key) && key.Length != 6)
            {
                return -1;
            }
            if (!string.IsNullOrEmpty(key))
            {
                mypicckey = Encoding.ASCII.GetBytes(key);
            }


            byte[] mypiccdata = new byte[48]; //卡数据缓冲

            status = piccreadex(myctrlword, mypiccserial, myareano, authmode, mypicckey, mypiccdata);

            byte[] guidarr=new byte[16];

            for(int i=0;i<guidarr.Length;i++)
            {
                guidarr[i]=mypiccdata[i];
            }

            uniqueID = new Guid(guidarr);

            return status;
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            GC.Collect();
        }
        #endregion
    }
}
